﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using DataPreparation;
using System.IO;
using Monitor;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace TMSimulator
{

    public partial class MainForm : Form
    {

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

        Monitor.MonitorForm monitor;
        List<string> readFiles;
        Dictionary<IntPtr, FileAccess> accessMap;
        List<string> selectedUsers;

        public MainForm()
        {
            InitializeComponent();
            readFiles = new List<string>();
            accessMap = new Dictionary<IntPtr, FileAccess>();
            selectedUsers = new List<string>();
            groupBox1.Enabled = false;
            groupBox2.Enabled = false;
            groupBox6.Enabled = false;
            label7.Visible = false;
            label8.Visible = false;
            checkedListBox1.CheckOnClick = true;
            this.txtLogFile.Text = "";
            label6.Text = "Distace matrix:FingerPrints, no factor was added";
            if (File.Exists(@"data\newSerializedDB.bin"))
                File.Delete(@"data\newSerializedDB.bin");

            if (!File.Exists(@"lib\GlobalCbtHook.dll"))
            {
                MessageBox.Show(@"could not find file: lib\GlobalCbtHook.dll" + Environment.NewLine + "Live monitor mode is disabled");
                chkMonitorLive.Enabled = false;
            }

            List<IFactor> factors = new List<IFactor>();
            try
            {
                Controller.InitializeSystem(@"data\logfile.txt", "fingerprints", factors, @"data\SerializedDB.bin");
            }
            catch (FileNotFoundException)
            {
                MessageBox.Show("Missing initial Log File. Make sure there is a data directory that contains an initial log file named logfile.txt", "Error Loading Data", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            UpdateUsers();
        }


        /// <summary>
        /// this function send parameters to the controller which init all the system
        /// </summary>
        /// <param name="path"></param>
        /// <param name="method"></param>
        /// <param name="factors"></param>
        public void Init(string path, string method, List<IFactor> factors, string DBpath)
        {
            try
            {
                Controller.InitializeSystem(@path, method, factors, DBpath);
                button7.Enabled = false;
                label8.Visible = true;
                label8.Refresh();
            }

            catch (Exception)
            {
                label8.Visible = false;
                label8.Update();
                button7.Enabled = true;
                button7.Update();
                MessageBox.Show("The files in the log file don't match the files in the database , please insert a different log file");
                txtLogFile.Text = "";
                txtLogFile.Update();

            }

            UpdateUsers();
        }

        private void UpdateUsers()
        {
            CheckedListBox.CheckedIndexCollection selected = checkedListBox1.CheckedIndices;
            List<string> selectedNames = new List<string>();
            for (int i = 0; i < selected.Count; i++)
            {
                selectedNames.Add(checkedListBox1.CheckedItems[i].ToString());
            }
            checkedListBox1.Items.Clear();
            chart1.Series.Clear();
            //sort the users by their name for easier viewing in the checkedListBox
            UserManager.UserIndex = (from entry in UserManager.UserIndex orderby entry.Key ascending select entry).ToDictionary(pair => pair.Key, pair => pair.Value);
            // show the users in the list box in the form

            foreach (string name in UserManager.UserIndex.Keys)
            {
                bool isSelected = selectedNames.Contains(name);
                if (isSelected)
                {
                    checkedListBox1.Items.Add(name, CheckState.Checked);
                }
                else
                {
                    checkedListBox1.Items.Add(name, CheckState.Unchecked);
                }
            }
        }

        public void ProcessNewFileAccess(FileAccess fileAccess)
        {
            Controller.HandleSingleFileAccess(fileAccess);
            UpdateUsers();
        }


        private void checkedListBox1_ItemCheck_1(object sender, ItemCheckEventArgs e)
        {
            //the name of the user to display
            string name = checkedListBox1.Items[e.Index].ToString();
            //if (chart1.Series.IndexOf(name) == -1) // if the user's graph not exist 
            if (e.CurrentValue == CheckState.Unchecked && e.NewValue == CheckState.Checked)
            {
                chart1.Series.Add(name);
                chart1.Series[name].ChartType = SeriesChartType.Line;  // graph type
                chart1.Series[name].MarkerStyle = MarkerStyle.Diamond; //label type
                chart1.Series[name].MarkerSize = 9; // marker size
                chart1.Series[name].BorderWidth = 3; // border size

                chart1.ChartAreas[0].AxisX.LabelStyle.Format = "   dd/MM/yyyy"; // format for display the labels
                // chart1.ChartAreas[0].AxisX.IntervalType = DateTimeIntervalType.Days; // the kind of interval on axis x
                // chart1.ChartAreas[0].AxisX.Interval = 1; //size of interval
                chart1.ChartAreas[0].AxisX.LabelStyle.Angle = 70;
                chart1.Series[name].XValueType = ChartValueType.DateTime; // kind of data in axis x
                chart1.ChartAreas["ChartArea1"].AxisX.Title = "Time"; // label for axis x
                chart1.ChartAreas["ChartArea1"].AxisY.Title = "TMscore"; // label for axis y


                foreach (KeyValuePair<FileAccess, double> pair in UserManager.UserIndex[name].History) //add the points of the user to the graph
                {
                    DateTime a = pair.Key.Time;
                    chart1.Series[name].Points.AddXY(new DateTime(a.Year, a.Month, a.Day, a.Hour, a.Minute, a.Second).ToOADate(), Math.Round(pair.Value, 3, MidpointRounding.AwayFromZero));
                }
            }
            else if (e.CurrentValue == CheckState.Checked && e.NewValue == CheckState.Unchecked)
            {
                chart1.Series.Remove(chart1.Series[name]);
            }

        }

        // display the tooltip in the graph
        private void chart1_GetToolTipText(object sender, ToolTipEventArgs e)
        {

            int j = 0;
            string newString = null;
            if (e.HitTestResult.ChartElementType == ChartElementType.DataPoint) // if this is an exist point in graph
            {
                int i = e.HitTestResult.PointIndex; // number of point in the graph
                string name = e.HitTestResult.Series.Name; // name of the user that the point belong to him
                string tool = name + " History:@";
                tool = tool.Replace("@", Environment.NewLine);
                foreach (KeyValuePair<FileAccess, double> pair in UserManager.UserIndex[name].History)
                {
                    if (j <= i)
                    {
                        // tooltip format
                        tool = tool + "Document Name:" + pair.Key.FilePath + "  Time: " + pair.Key.Time + " Document TMscore:" + Math.Round(Controller.db.items[pair.Key.FilePath].getTMScore(), 3, MidpointRounding.AwayFromZero) +
                             " accumulative TMscore: " + Math.Round(pair.Value, 3, MidpointRounding.AwayFromZero) + "@";
                        newString = tool.Replace("@", Environment.NewLine);
                        j++;
                    }
                }
                DataPoint dp = e.HitTestResult.Series.Points[i];
                e.Text = newString;
            }
        }

        // Browse folder button click in DB Initialization tab
        private void button1_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {
                this.txtSourceFolder.Text = folderBrowserDialog1.SelectedPath;
            }
        }

        // Run button click in DB Initialization tab
        private void button4_Click(object sender, EventArgs e)
        {

            string initOption = "folder";
            if (radioButton4.Checked)
                initOption = "file";
            if (radioButton5.Checked)
                initOption = "preparedfile";


            string path;

            switch (initOption)
            {
                case "preparedfile":
                    MessageBox.Show("documents updated successfully, Please insert a new Log file");
                    tabControl1.SelectedTab = tabPage2;
                    tabControl1.Update();
                    break;

                case "folder":

                    this.Update();
                    path = this.txtSourceFolder.Text;
                    if (path.Equals(""))
                    {
                        MessageBox.Show("No source folder was selected");
                        return;
                    }
                    Dictionary<string, string> readDocs = DocumentsReader.ReadAlltxtFile(@path); //read all files
                    List<IItem> allDoc = new List<IItem>(DocumentsParser.ParserFolder(readDocs)); //parser files and return list of items
                    DBGenerator d = new DBGenerator();
                    label7.Visible = true;
                    label7.Update();
                    d.GenerateDB(allDoc, @"data\newSerializedDB.bin");
                    List<string> factor = new List<string>();
                    chart1.Series.Clear();
                    checkedListBox1.Items.Clear();
                    label7.Visible = false;
                    label7.Update();
                    SaveOption();
                    label8.Visible = false;
                    label8.Update();
                    txtLogFile.Text = "";
                    break;

                case "file":   // option of reading an input file that contains rows in format: [filepath,tmscore]

                    this.Update();
                    path = this.txtSourceFile.Text;
                    if (path.Equals(""))
                    {
                        MessageBox.Show("No source file was selected");
                        return;
                    }

                    Dictionary<string, string> texts = new Dictionary<string, string>();  // key= doc name, value = doc text
                    Dictionary<string, double> tmscores = new Dictionary<string, double>(); // key = doc name, value = tmscore
                    string firstLine;
                    string line;
                    StreamReader file = new StreamReader(path);
                    StreamReader sr;
                    while ((line = file.ReadLine()) != null)      // reading the input file
                    {
                        try
                        {
                            string[] lineArr = line.Split(',');
                            string filePath = lineArr[0];
                            string fileName = filePath.Split('\\').Last();
                            double tmScore = Convert.ToDouble(lineArr[1]);
                            tmscores[fileName] = tmScore;
                            if (Path.GetExtension(filePath).CompareTo(".Mail") == 0)
                            {
                                sr = new StreamReader(filePath);
                                firstLine = sr.ReadLine();
                                texts[fileName] = sr.ReadToEnd();
                            }
                            else
                            {
                                string text = File.ReadAllText(filePath);
                                texts[fileName] = text;
                            }
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show("Error while reading the Initialization File. Check for correct file Format");
                            return;
                        }
                    }
                    file.Close();
                    List<IItem> docs = new List<IItem>(DocumentsParser.ParserFile(texts, tmscores)); //parser files and return list of items
                    DBGenerator db = new DBGenerator();
                    label7.Visible = true;
                    label7.Update();
                    db.GenerateDB(docs, @"data\newSerializedDB.bin");
                    List<string> factors = new List<string>();
                    chart1.Series.Clear();
                    checkedListBox1.Items.Clear();
                    label7.Visible = false;
                    label7.Update();
                    label8.Visible = false;
                    label8.Update();
                    txtLogFile.Text = "";
                    SaveOption();
                    break;
            }
        }

        // Browse button click in Simulator tab
        private void btnBrowseLogFile_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = "Text files (*.txt)|*.txt";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                this.txtLogFile.Text = openFileDialog1.FileName;
            }
            button7.Enabled = true;
            label8.Visible = false;
            label8.Update();
        }

        // Run button click in Simulator tab
        private void button7_Click(object sender, EventArgs e)
        {
            txtSourceFile.Text = "";
            txtSourceFolder.Text = "";
            string method = "fingerprints";
            List<IFactor> factors = new List<IFactor>();
            string filePath = this.txtLogFile.Text;
            if (filePath.Equals(""))
            {
                MessageBox.Show("No log file was selected");
                return;
            }

            if (this.radioButton2.Checked)
                method = "cosine";

            if (chkExposureTime.Checked)
                factors.Add(new ExposureTimeFactor());

            if (!filePath.Equals(""))
            {
                chart1.Series.Clear();
                checkedListBox1.Items.Clear();

                if (radioButton5.Checked)
                    Init(filePath, method, factors, @txtPreparedFile.Text);
                else
                {
                    if (File.Exists(@"data\newSerializedDB.bin"))
                        Init(filePath, method, factors, @"data\newSerializedDB.bin"); // if a new file db was created
                    else
                        Init(filePath, method, factors, @"data\SerializedDB.bin");    // the initial db
                }
                if (factors.Count == 0)
                    label6.Text = "Distace matrix:" + method + ", no factor was added";
                else
                    label6.Text = "Distace matrix:" + method + ", exposureTime factor was added";
            }

        }

        private void folderOption_CheckedChanged(object sender, EventArgs e)
        {
            this.txtSourceFile.Clear();
            groupBox1.Enabled = true;
            button1.Enabled = true;
            groupBox2.Enabled = false;
            groupBox6.Enabled = false;

        }
        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            button7.Enabled = true;
            label8.Visible = false;
            label8.Update();
        }

        private void radioButton2_CheckedChanged(object sender, EventArgs e)
        {
            button7.Enabled = true;
            label8.Visible = false;
            label8.Update();
        }

        private void chkExposureTime_CheckedChanged(object sender, EventArgs e)
        {
            button7.Enabled = true;
            label8.Visible = false;
            label8.Update();
        }


        //event of selecting folder radiobutten
        private void radioButton4_CheckedChanged(object sender, EventArgs e)
        {
            this.txtSourceFolder.Clear();
            this.txtPreparedFile.Clear();
            groupBox1.Enabled = false;
            groupBox6.Enabled = false;
            btnBrowseInitFile.Enabled = true;
            groupBox2.Enabled = true;
        }

        // browse init file in initialization tab
        private void btnBrowseInitFile_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = "Text files (*.txt)|*.txt";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                this.txtSourceFile.Text = openFileDialog1.FileName;
            }
        }

        private void closeButten_Click(object sender, EventArgs e)
        {
            this.Close();
        }


        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (File.Exists(@"data\newSerializedDB.bin"))
                File.Delete(@"data\newSerializedDB.bin");
        }

        private void tabPage3_Enter(object sender, EventArgs e)
        {
            chkMonitorLive.Visible = true;
        }

        private void tabPage3_Leave(object sender, EventArgs e)
        {
            chkMonitorLive.Visible = false;
        }

        private void chkMonitorLive_CheckedChanged(object sender, EventArgs e)
        {
            if (chkMonitorLive.Checked)
            {
                Controller.WriteMonitorToLog = true;
                monitor = new Monitor.MonitorForm();
                monitor.NewNotepadPPEntry += new EventHandler(monitor_NewNotepadPPEntry);
                monitor.ClosedNotepadPPEntry += new EventHandler(monitor_ClosedNotepadPPEntry);
            }
            else
            {
                Controller.WriteMonitorToLog = false;
                monitor.Close();
            }
        }

        /// <summary>
        /// This method is run when:
        /// a. live monitor mode is on
        /// b. a notepad++ window is closed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void monitor_ClosedNotepadPPEntry(object sender, EventArgs e)
        {
            if (e.GetType() == typeof(MyEventArgs))
            {
                MyEventArgs me = (MyEventArgs)e;
                if (accessMap.ContainsKey(me.getWindowHandle()))
                {
                    FileAccess closedAccess = accessMap[me.getWindowHandle()];

                    closedAccess.ExposureDuration = new TimeSpan(DateTime.Now.Ticks - closedAccess.Time.Ticks);

                    ProcessNewFileAccess(closedAccess);
                }
            }
        }

        /// <summary>
        /// This method is run when:
        /// a. live monitor mode is on
        /// b. a new window 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void monitor_NewNotepadPPEntry(object sender, EventArgs e)
        {
            if (e.GetType() == typeof(MyEventArgs))
            {
                MyEventArgs me = (MyEventArgs)e;
                Process[] npp = Process.GetProcessesByName("notepad++");

                if (npp.Length > 0)
                {
                    string title = npp[0].MainWindowTitle;
                    if (title.Length < 12)
                    {
                        return;
                    }
                    title = title.Substring(0, title.Length - 12);

                    if (title != "" && !readFiles.Contains(title))
                    {
                        readFiles.Add(title);

                        FileAccess newFileAccess = new FileAccess();

                        newFileAccess.Time = DateTime.Now;
                        newFileAccess.Username = System.Environment.GetEnvironmentVariable("USERNAME");
                        newFileAccess.FileAuthor = System.IO.File.GetAccessControl(title).GetOwner(typeof(System.Security.Principal.NTAccount)).ToString();
                        newFileAccess.FileExposurePercent = 1;
                        newFileAccess.FilePath = Path.GetFileName(title);

                        if (!accessMap.ContainsKey(me.getWindowHandle()))
                        {
                            accessMap.Add(me.getWindowHandle(), newFileAccess);
                        }
                    }
                }
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            openFileDialog2.Filter = "Bin files (*.bin)|*.bin";
            if (openFileDialog2.ShowDialog() == DialogResult.OK)
            {

                this.txtPreparedFile.Text = openFileDialog2.FileName;
            }
        }

        private void radioButton5_CheckedChanged(object sender, EventArgs e)
        {
            groupBox6.Enabled = true;
            this.txtSourceFile.Clear();
            this.txtSourceFolder.Clear();
            groupBox1.Enabled = false;
            groupBox2.Enabled = false;
        }


        public void SaveOption()
        {
            if (MessageBox.Show("documents updated successfully." + Environment.NewLine + "Do you want to save the DB file?", "Save", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                saveFileDialog1.Filter = "Bin files (*.bin)|*.bin";

                if (saveFileDialog1.ShowDialog() == DialogResult.OK)
                {
                    try
                    {
                        File.Copy(@"data\newSerializedDB.bin", saveFileDialog1.FileName);
                    }
                    catch (Exception)
                    {
                        MessageBox.Show("File name already exist. please select an other name");
                        return;
                    }

                    MessageBox.Show("file saved successfully, Please insert a new Log file");
                    tabControl1.SelectedTab = tabPage2;
                    groupBox3.Enabled = true;
                    tabControl1.Update();
                }
            }

            else

            MessageBox.Show("Please insert a new Log file");
            tabControl1.SelectedTab = tabPage2;
            groupBox3.Enabled = true;
            tabControl1.Update();
        }
    }
}

